package nachos.threads;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import nachos.machine.*;

/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 * 使用硬件定时器提供抢占，并且允许线程睡眠一定的时间。
 */
public class Alarm {
    /**
     *分配一个Alarm,设置机器的定时器中断处理器作为这个alarm的回调处理器。
     *<p>
     *注意：
     *<p>
     *当构造多个alarm时，nachos可能不会正常工作。
     *<p>
     * Allocate a new Alarm. Set the machine's timer interrupt handler to this
     * alarm's callback.
     * <p><b>Note</b>: Nachos will not function correctly with more than one
     * alarm.
     * 
     */

    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });
	map = new HashMap<KThread,Long>();
    }
	private HashMap<KThread,Long> map=null ;
    /**
     * 定时器中断处理器。这个函数被机器定时器约每500个ticks调用一次。
     * 这会使当前线程yield，如果有线程该被运行，强制执行一次上下文切换。
     * <p>
     * The timer interrupt handler. This is called by the machine's timer
     * periodically (approximately every 500 clock ticks). Causes the current
     * thread to yield, forcing a context switch if there is another thread
     * that should be run.
     */
    public void timerInterrupt() {
    	   /*******************************/
   // 	System.out.println("beging alarm time interrupt with the time is "+Machine.timer().getTime());
    	boolean status = Machine.interrupt().disable();
    	Set<KThread>  set = map.keySet();
    	Iterator<KThread> iter = set.iterator();
    	KThread kt = null;
    	List<KThread> list = new ArrayList<KThread>();
    	Long time = Machine.timer().getTime();
    	while(iter.hasNext())
    	{
    		kt = iter.next();
    		long times = map.get(kt);
    		if(times<=time)
    		{
    			list.add(kt);
    		}
    	//	kt = iter.next();
    	}
    	if(!list.isEmpty())
    	{
    		int i=0;
    		for(;i<list.size();i++)
    		{
    			map.remove(list.get(i));
    			list.get(i).ready();
    		}
    	}
        /*******************************/
    	KThread.currentThread().yield();
        /*******************************/
    	Machine.interrupt().restore(status);
    	   /*******************************/

    }

    /**
     *使当前线程睡眠至少x个ticks，在定时器中断处理函数中唤醒它。这个线程必须在中断第一次发生(current time) >= (WaitUntil called time)+(x)时被唤醒。
     *<p>
     * Put the current thread to sleep for at least <i>x</i> ticks,
     * waking it up in the timer interrupt handler. The thread must be
     * woken up (placed in the scheduler ready set) during the first timer
     * interrupt where
     * <p><blockquote>
     * (current time) >= (WaitUntil called time)+(x)
     * </blockquote>
     *
     * @param	x	the minimum number of clock ticks to wait.
     *
     * @see	nachos.machine.Timer#getTime()
     */
    public void waitUntil(long x) {
	// for now, cheat just to get something working (busy waiting is bad)
    	
	long wakeTime = Machine.timer().getTime() + x;
    /*******************************/
	boolean status = Machine.interrupt().disable();
	map.put(KThread.currentThread(), wakeTime);
	KThread.currentThread().sleep();
	Machine.interrupt().restore(status);
    /*******************************/
	/*
	 *	this code is bad,so we  change
	while (wakeTime > Machine.timer().getTime())
	    KThread.yield();
	  */
    }

}
